﻿
/**
 * 
 * DataGridWithFixedColumn
 * This component extends the DataGrid. It can contain a column that is always displayed even when the dataProvider change
 * and can't be moved when another column is dragged or added.
 * 
 * @author
 * Arnaud FOUCAL - http://afoucal.free.fr - afoucal@free.fr
 * 
 * @licence
 * This component is free for use, modification and distribution under the following condition:
 * 	Just mention the name and the url of the author in a part of your product that is visible to the user (about section, documentation...)
 * 
 * You can avoid this obligation with a donation.
 * 
 * @version 1.0
 * 1st release
 * 
 * @version 1.0.1
 * - Fixed issue generating an error when columns is empty
 */


package com 
{
	
	import mx.controls.DataGrid;
	import mx.controls.dataGridClasses.DataGridColumn;
	import flash.events.Event;
	import mx.events.CollectionEvent;
	import mx.events.CollectionEventKind;
	import mx.core.mx_internal;
	use namespace mx_internal;
	

	public class DataGridWithFixedColumn extends DataGrid
	{
		
		public static const FIXEDCOLUMN_POSITION_FIRST:String = "fixedColumnPositionFirst";
		public static const FIXEDCOLUMN_POSITION_LAST:String = "fixedColumnPositionLast";
		public static const FIXEDCOLUMN_POSITION_OFF:String = "fixedColumnPositionOff";
	
		
		/*///////////////////////////////////////////////////
		 * 
		 * PROPERTIES
		 * 
		 *////////////////////////////////////////////////////
		 
		/*
		 * Indicate if the fixed column has already been added or not
		 */
		private var _fixedColumnExist:Boolean = false;
		
		
		/**
		 * Set the fixed column to be added to the dataGrid.
		 * The column is a DataGridColumn.
		 */
		private var _fixedColumn:DataGridColumn;
		public function get fixedColumn():DataGridColumn { return _fixedColumn; }
		public function set fixedColumn( value:DataGridColumn ):void
		{	
			value.visible = _showFixedColumn;
			value.draggable = false;
			value.sortable = false;
			_fixedColumn = value;
			
			// call the construction of columns so that the new column can be displayed
			_fixedColumnExist = false;
			// catch error in the case of mxml-defined column
			try
			{
				var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
				event.kind = CollectionEventKind.RESET;
				collectionChangeHandler(event);
			}
			catch (e:Error ) {}
			
			// colNum may be NaN when a column has been manually added and the dataProvider changed.
			// so the fixedColumn can't be replaced any more.
			// this part address this an force the fixedColumn to be updated in this specific configuration
			if ( isNaN( _fixedColumn.colNum ) && getFixedColumnIndex()>-1)
			{
				// avoid the addition of a duplicated fixedColumn
				_fixedColumnExist = true;
				
				// replace the existing fixedColumn with the new one
				var temp:Array = columns;
				temp[ getFixedColumnIndex() ] = _fixedColumn;
				columns = temp;
			}
		}
		
		
		/**
		 * Define the position of the fixed column in the array of column.
		 * The fixed column will always have the defined position.
		 * If set to 'off', other columns can be dropped before or after the fixed column
		 */
		private var _fixedColumnPosition:String = FIXEDCOLUMN_POSITION_LAST;
		public function get fixedColumnPosition():String { return _fixedColumnPosition; }
		public function set fixedColumnPosition( value:String ):void 
		{
			// move the fixed column to the right position according to the fixedColumnPosition value;
			switch (value)
			{
				case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_LAST:
					shiftColumns( getFixedColumnIndex(), columns.length-1 );
					break;
				
				case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_FIRST:
					shiftColumns( getFixedColumnIndex(), 0 );
					break;
			}

			_fixedColumnPosition = value;
		}
		
		
		/**
		 * Indicate that the fixed column must be shown or not
		 */
		private var _showFixedColumn:Boolean = true;
		public function get showFixedColumn():Boolean { return _showFixedColumn; }
		public function set showFixedColumn( value:Boolean ):void 
		{
			_showFixedColumn = value;

			if (_fixedColumn && columns.length > 0)
				columns[ getFixedColumnIndex() ].visible = value;
		}
		
		
		/*///////////////////////////////////////////////////
		 * 
		 * CONSTRUCTOR
		 * 
		 *////////////////////////////////////////////////////

		public function DataGridWithFixedColumn() 
		{
			super();
			
			// Add a listeners that listen to changes in the columns property (column added, deleted...)
			addEventListener( 'columnsChanged', addFixedColumn );
		}

		
		/*///////////////////////////////////////////////////
		 * 
		 * METHODS
		 * 
		 *////////////////////////////////////////////////////
		
		/**
		 * @private
		 * 
		 * Add the column only if 
		 * - it is not already added
		 * - it is required to show it
		 * - the fixedColumn is not null
		 */	
		private function addFixedColumn( e:Event = null ):void
		{
			if (!_fixedColumnExist && _fixedColumn)
			{
				// indicate that the column has ben added, avoiding a new addition
				_fixedColumnExist = true;
				
				// save and add the column to the column array depending on the position
				var col:Array = columns;
				
				switch (_fixedColumnPosition)
				{
					case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_LAST:
						col.push( _fixedColumn );
						break;
					case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_FIRST:
						col.unshift( _fixedColumn );
						break;
					case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_OFF:
						col.push( _fixedColumn );
						break;
				}
				
				columns = col;
			}
		}
		
		
		/**
		 * Returns the index of the fixed column in the array of the columns
		 * 
		 * @return
		 */
		public function getFixedColumnIndex() :int
		{
			var index:int;
			
			if ( _fixedColumn )
				switch (_fixedColumnPosition)
				{
					case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_LAST:
						index = columns.length - 1;
						break;
						
					case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_FIRST:
						index = 0;
						break;
						
					default:
						index = columns.indexOf( _fixedColumn );
						break;
				}
				
			return index;
		}
		
		
		/*///////////////////////////////////////////////////
		 * 
		 * OVERRIDES
		 * 
		 *////////////////////////////////////////////////////
		
		override public function set dataProvider(value:Object):void
		{
			super.dataProvider = value;
			
			_fixedColumnExist = false;
		}
		
		/**
		 *  @private
		 * 
		 * Overriden to check and correct the position of the fixed column
		 */
		override public function set columns( value:Array ):void
		{
			var col:Array = value;
			var len:int = value.length;
			var fci:int = value.indexOf( _fixedColumn );

			switch (_fixedColumnPosition)
			{
				case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_LAST:
					// if the fixed column is not at the end
					if ( fci != -1 && fci < len - 1 )
					{
						// remove it
						value.splice( fci, 1 );
						// add it again at the end
						value[len-1] = _fixedColumn;
					}
					break;
					
				case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_FIRST:
					// if the fixed column is not the very first column
					if ( fci > 0 )
					{
						// remove it
						value.splice( fci, 1 );
						// add it again at the very beginning
						value.unshift( _fixedColumn );
					}
					break;
			}

			super.columns = value;
		}
		
		
		/**
		 *  Move a column to a new position in the columns array, shifting all
		 *  other columns left or right and updating the sortIndex and lastSortIndex variables accordingly.
		 *  The fixed column can't be shifted if its position is set to first or last index.
		 */
		override mx_internal function shiftColumns( oldIndex:int, newIndex:int, trigger:Event = null ):void
		{
			var fci:int = getFixedColumnIndex();
			
			// Only if the shifted column (oldindex) is not the fixed column.
			// This occurs when the property fixedColumnPosition is set
			if ( oldIndex != fci )
			{
				// change values of newIndex in order to keep the fixed column always at the same index
				switch (_fixedColumnPosition)
				{
					case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_LAST:
						if ( newIndex == fci )
							newIndex--;
						break;
					
					case DataGridWithFixedColumn.FIXEDCOLUMN_POSITION_FIRST:
						if ( newIndex == 0 )
							newIndex = 1;
						break;
				}
			}
			
			super.shiftColumns(oldIndex, newIndex, trigger);
		}
	}
}